/**
 * @widoc $.fn.initDateLine
 * @namespace comp
 * @des 初始化时间范围选择控件，依赖 WdatePicker
 * @type function
 * @demo initDateLine/demo0 通用日期范围选择
 * @param {object} opts 参数配置
 * @param {Date=} opts.today 当天日期对象，默认为客户端本地时间
 * @param {Date=} opts.startDate 初始化时的起始日期，若定义 allowEmpty:false，则默认为 today 当月第一天
 * @param {Date=} opts.endDate 初始化时的结束日期，若定义 allowEmpty:false，则默认为 startDate 起始日期的这个月的最后一天
 * @param {boolean=} opts.allowEmpty 是否允许空，默认为 true
 * @param {function=} opts.onChange 日期改变时的回调方法，onChange(start,end)
 * - {Date} start
 * - {Date} end
 * @return {object} obj 返回对象
 * @rtn {Date} obj.curStart 开始日期对象
 * @rtn {Date} obj.curEnd 结束日期对象
 * @rtn {function} obj.fSetDate(startDate,endDate) 设置日期范围 TODO 作为接口调用时，不应默认触发回调 opts.onChange
 * - {Date} startDate
 * - {Date} endDate
 * @rtn {function} obj.fGetDate() 获取当前组件中的日期范围
 * - 返回值：{ curStart:{Date}, curEnd:{Date} }
 */
$.fn.initDateLine=function(opts){
    if(!window.WdatePicker) return null;
    opts= $.extend({
        today:new Date(),
        //startDate,endDate - {Date=}
        allowEmpty: true,
        onChange: $.noop
    },opts);
    var dateline=$(this);
    var dtGuid= $.generateGuid('dateline');
    // dateline 主要元素：上个月、下个月、弹出、主体
    var prevEl,nextEl,triggerEl,layerEl,
        jqStartIpt,// custom 弹出层的开始时间文本框
        jqEndIpt;// custom 弹出层的结束时间文本框
    var oCurStart, //保存当前设置的开始时间
        oCurEnd,//保存当前设置的结束时间
        api;
    var _monYear;// month 中显示的年份
    /**
     * 设置 custom 中 datepicker 文本框显示的起始时间
     * @param startDate {Date=} 开始时间，未定义时，设为 ''
     * @param endDate {Date=} 结束时间，未定义时，设为 ''
     */
    var fSetDtCustom=function(startDate, endDate){
        jqStartIpt.val(startDate ? startDate.format('yyyy-MM-dd') : '');
        jqEndIpt.val(endDate ? endDate.format('yyyy-MM-dd') : '');
    };
    /**
     * 重置显示文本框时间和弹出框的时间
     * @param startDate {Date=} 开始时间，未定义时，根据当前时间设为本月第一天
     * @param endDate {Date=} 结束时间，未定义时，根据开始时间设为当月最后一天
     * @param bTrigger {boolean=} 若日期发生变化，是否触发回调，默认为 false（一般组件内部的方法才设为 true，api.fSetDate() 则不回调，应由用户自行触发，2017.08.09 by qq）
     */
    var fSetDate = function(startDate, endDate, bTrigger){
        var oldStart = oCurStart,
            oldEnd = oCurEnd;
        var spans=triggerEl.children('span');
        // 处理开始日期和结束日期。显示顺序（开始日期不得超过结束日期），不允许为空时需补全日期
        var fDealDate = function(){
            var temp;
            if(startDate && endDate){// 开始时间和结束时间都存在
                if(startDate > endDate){ //判断时间大小
                    temp = startDate;
                    startDate = endDate;
                    endDate = temp;
                }
            }else{
                if(!opts.allowEmpty){//不能为空
                    if(startDate){// startDate 存在，endDate 不存在
                        endDate = new Date(startDate.add('M',1).add('d',-1));
                    }
                    else if(endDate){// startDate 不存在，endDate 存在
                        startDate = new Date(endDate.add('M',-1).add('d',1));
                    }
                    else{// startDate 不存在，endDate 不存在，范围取当月
                        startDate = new Date(opts.today.format('yyyy/MM/01'));
                        endDate = new Date(startDate.add('M',1).add('d',-1));
                    }
                }
            }
        };
        // TODO del 旧版本曾支持 yyyy/MM/dd 的字符串输入，此处两个 if 判断仅作兼容，升版本时将移除
        if(typeof startDate == 'string'){
            startDate = new Date(startDate);
        }
        if(typeof endDate == 'string'){
            endDate = new Date(endDate);
        }
        // 参数处理
        fDealDate();
        // 转为 string 筛除时分秒等无用信息后转回 Date，避免影响判断
        if(startDate){
            startDate = new Date(startDate.format('yyyy/MM/dd'));
        }
        if(endDate){
            endDate = new Date(endDate.format('yyyy/MM/dd'));
        }
        // 同步到组件全局变量，及 api 接口
        api.curStart = oCurStart = startDate;
        api.curEnd = oCurEnd = endDate;
        // 同步到主体显示
        spans.eq(0).html(startDate ? startDate.format('yyyy年MM月dd日') : '----年--月--日');
        spans.eq(2).html(endDate ? endDate.format('yyyy年MM月dd日') : '----年--月--日');
        hideLayer();// 设置日期后隐藏弹框，因弹框中的日期将在弹出时才进行同步，所以此时若不隐藏，可能造成主体与弹框不统一的情况
        // 日期发生了变化，作为接口调用时，不应默认触发回调 opts.onChange
        if(bTrigger &&
            (oldStart != startDate || oldEnd != endDate)){
            opts.onChange(oCurStart, oCurEnd);
        }
    };
    // 获取日期
    var fGetDate = function(){
        return {
            curStart:oCurStart,
            curEnd:oCurEnd
        }
    };
    // 隐藏弹框
    var hideLayer=function(){// 隐藏主体
        triggerEl.removeClass('now');
        layerEl.removeClass('now');
        $(document).off('click.hide'+dtGuid);
    };
    // 显示弹框
    var showLayer=function(){// 显示主体
        fSetDtCustom(oCurStart, oCurEnd); //防止在弹出层清空或修改文本框后，没有点确定按钮确定时间关闭弹框，而是点document其他地方关闭了弹框，此时需要同步时间
        triggerEl.addClass('now');
        var triggerBCR=triggerEl[0].getBoundingClientRect();
        layerEl
            .addClass('now')
            .css({
                left:triggerEl.position().left+'px',
                top:(triggerEl.position().top+triggerBCR.bottom-triggerBCR.top)+'px'
            });
        // 点击其他部位隐藏
        $(document).on('click.hide' + dtGuid, function(e){
            if($(e.target).closest(dateline).length){
                return;
            }
            hideLayer();
        });
        layerEl.children('.date-select').find('.date-option').eq(0).trigger('click.datebody'+dtGuid);// 默认选中自定义选择
    };
    /* custom 快捷切换 */
    var initCustom=function(customEl){
        var hideCustom=function(el){// 隐藏 custom 下拉
            el.find('.dateline-layer').removeClass('now');
        };
        var showCustom=function(el){// 显示 custom 下拉
            var custom=el.find('.dateline-layer');
            var triggerBCR=el[0].getBoundingClientRect();
            custom
                .addClass('now')
                .css({
                    left:(el.position().left+5)+'px',// 含有 5px 外边距
                    top:(el.position().top+triggerBCR.bottom-triggerBCR.top-1)+'px'
                });
        };
        // 快速选择弹框选中事件
        var fCustomClick = function(){// custom 快捷切换
            var opt=$(this)
                ,val=opt.attr('data-opt')
                ,txt=opt.html();
            opt.closest('.dateline-layer').prev().html(txt);
            var today=opts.today,
                y,w,start;
            switch(val){
                case 'thisMonth':// 本月
                    start=new Date(opts.today.format('yyyy/MM/01'));
                    fSetDtCustom(start, start.add('M',1).add('d',-1));
                    break;
                case 'lastMonth':// 上月
                    start=new Date(today.format('yyyy/MM/01')).add('M',-1);
                    fSetDtCustom(start, start.add('M',1).add('d',-1));
                    break;
                case 'thisWeek':// 本周 - 周一为第一天
                    w=(today.getDay() || 7)-1;
                    start=today.add('d',-w);
                    fSetDtCustom(start, start.add('d',6));
                    break;
                case 'lastWeek':// 上周
                    w=(today.getDay() || 7)-1;
                    start=today.add('d',-w-7);
                    fSetDtCustom(start, start.add('d',6));
                    break;
                case 'thisQ':// 本季
                    start=new Date(today.getFullYear()+'/'+(Math.floor(today.getMonth()/3)*3+1)+'/01');
                    fSetDtCustom(start, start.add('M',3).add('d',-1));
                    break;
                case 'lastQ':// 上季
                    start=new Date(today.getFullYear()+'/'+(Math.floor(today.getMonth()/3)*3+1)+'/01');
                    fSetDtCustom(start.add('M',-3), start.add('d',-1));
                    break;
                case 'thisYear':// 今年
                    y=today.getFullYear();
                    fSetDtCustom(new Date(y+'/01/01'), new Date(y+'/12/31'));
                    break;
                case 'lastYear':// 去年
                    y=today.getFullYear()-1;
                    fSetDtCustom(new Date(y+'/01/01'), new Date(y+'/12/31'));
                    break;
                case 'last7Days':// 近7天
                    fSetDtCustom(today.add('d',-6), today);
                    break;
                case 'last30Days':// 近30天
                    fSetDtCustom(today.add('d',-29), today);
                    break;
            }
        };
        // custom 底部按钮事件
        var fCustomBtnClick = function(){
            var jqThis = $(this);
            var fGetIptDate;// 输入框的值的处理
            switch(jqThis.attr('data-type')){
                case 'submit':// 确认，根据 datepicker 文本框赋值
                    fGetIptDate=function(data){// 输入框的值的处理
                        return data ? new Date(data.replace(/-/g,'/')) : undefined;
                    };
                    fSetDate(fGetIptDate(jqStartIpt.val()), fGetIptDate(jqEndIpt.val()), true);
                    break;
                case 'clean':// 清空
                    fSetDate(undefined, undefined, true);
                    break;
            }
        };
        jqStartIpt.click(function(e){
            WdatePicker({ dateFmt:'yyyy-MM-dd' ,readOnly:true},false,e);
        });
        jqEndIpt.click(function(e){
            WdatePicker({ dateFmt:'yyyy-MM-dd',readOnly:true},false,e);
        });
        // 显示/隐藏快捷选择弹框
        customEl.find('.date-custom-trigger').click(function(){
            var el=$(this);
            el.find('.dateline-layer').hasClass('now') ? hideCustom(el) : showCustom(el);
        });
        customEl.find('.date-custom-select')
                .append(
                    '<li data-opt="thisMonth">本月</li>' +
                    '<li data-opt="lastMonth">上月</li>' +
                    '<li data-opt="thisWeek">本周</li>' +
                    '<li data-opt="lastWeek">上周</li>' +
                    '<li data-opt="thisQ">本季</li>' +
                    '<li data-opt="lastQ">上季</li>' +
                    '<li data-opt="thisYear">今年</li>' +
                    '<li data-opt="lastYear">去年</li>' +
                    '<li data-opt="last7Days">近7天</li>' +
                    '<li data-opt="last30Days">近30天</li>')
                // 快速选择弹框选中事件
                .on('click','li',fCustomClick);
        // custom 底部按钮
        if(!opts.allowEmpty){// 禁止为空时，隐藏清空按钮
            customEl.find('.date-btn[data-type="clean"]').css('display','none');
        }
        customEl.on('click.btn','.date-btn',fCustomBtnClick);
    };
    /* month 快捷切换 */
    var initMonth=function(monthEl){
        /**
         * 设置 month 年份，并根据年份设置月份状态
         * @param year {int=} 年份，未定义时为今年
         */
        var setMonthYear=function(year){
            var today = opts.today
                ,thisYear = today.getFullYear()
                ,thisMonth = today.getMonth()+1;
            if(!year) year= thisYear;
            _monYear=year;
            monthEl.find('.date-month-head>div').eq(1).html(year+'年');
            var monthStr='';
            for(var i= 1,clsStr;i<=12;i++){
                clsStr='';
                if(year==thisYear && i==thisMonth){// 当前月
                    clsStr=' class="currentMonth"';
                }else if(year==thisYear && i>thisMonth || year>thisYear){// 不可选的月份
                    clsStr=' class="cannotSelect"';
                }
                monthStr+='<li data-month="'+ i +'"' + clsStr
                    + (i==1||i==7 ? ' style="margin-left:1px;"' : '') +'>'+ i +'月</li>';
            }
            monthEl.children('.date-month-body').html(monthStr);
        };
        var fMonthHeadClick = function(){
            if($(this).hasClass('dateline-l')){// 上一年
                setMonthYear(_monYear-1);
            }else if($(this).hasClass('dateline-r')){// 下一年
                setMonthYear(_monYear+1);
            }
        };
        var fMonthBodyClick = function(){
            if($(this).hasClass('cannotSelect')) return;// 不可选的月份
            var oNewStart = new Date(_monYear+'/'+$(this).attr('data-month')+'/01'),
                oNewEnd = new Date(oNewStart.add('M', 1).add('d', -1)); // 结束日期设为开始日期加1个月减1天
            fSetDate(oNewStart, oNewEnd, true);
        };
        setMonthYear();
        monthEl.children('.date-month-head').on('click.month','>div',fMonthHeadClick);
        monthEl.children('.date-month-body').on('click.month','li',fMonthBodyClick);
    };
    var initBody=function(bodyEl){
        // 初始化弹出主体中切换 body 的事件 （自定义选择或按月选择的tab切换）
        var fDateOptionClick = function(){
            bodyEl.find('.dateline-layer').removeClass('now');// 切换时将 date-body 中的弹出层隐藏
            var trigger=$(this);
            if(trigger.hasClass('now')) return;
            layerEl.find('.date-select>.now').removeClass('now');
            trigger.addClass('now');
            bodyEl.children('.now').removeClass('now');
            bodyEl.children('[data-type="'+trigger.attr('data-type')+'"]').addClass('now');
        };
        bodyEl.children('.date-select')
              .off('click.datebody'+dtGuid)
              .on('click.datebody'+dtGuid,'.date-option',fDateOptionClick);//自定义选择或按月选择的tab切换
        // showLayer() 显示弹框时，默认点击第一项
    };
    var initDateLine=function(){
        // 初始化el元素
        (function initEl(){
            prevEl=$('<div class="dateline-l"></div>');
            triggerEl=$('<div class="dateline-c date-trigger">' +
                '<span></span>' +
                '<span style="width:auto;">-</span>' +
                '<span></span>' +
                '</div>');
            nextEl=$('<div class="dateline-r"></div>');
            layerEl=$('<div class="dateline-layer date-body">' +
                '<div class="date-select clearf">' +
                '<div data-type="custom" class="date-option now">自定义选择</div>' +
                '<div data-type="month" class="date-option">按月选择</div>' +
                '</div>' +
                '<div data-type="custom" class="date-container now">' +
                '<div class="date-line clearf">' +
                '<div class="date-trigger date-custom-trigger"><span>本月</span>' +
                '<div class="dateline-layer"><ul class="date-custom-select"></ul></div>' +
                '</div>' +
                    //'<input class="date-trigger" onclick="WdatePicker({ dateFmt:\'yyyy-MM-dd\',readOnly:true});" /><span>-</span><input class="date-trigger" onclick="WdatePicker({ dateFmt:\'yyyy-MM-dd\',readOnly:true});" />' +
                '<input class="date-trigger" /><span>-</span><input class="date-trigger" />' +
                '</div>' +
                '<div class="date-line clearf">' +
                '<div class="date-btn" data-type="submit">确认</div>' +
                '<div class="date-btn" data-type="clean">清空</div>' +
                '</div>' +
                '</div>' +
                '<div data-type="month" class="date-container">' +
                '<div class="date-line clearf date-month-head">' +
                '<div class="dateline-l"></div>' +
                '<div style="width:80px;text-align: center;"></div>' +
                '<div class="dateline-r"></div>' +
                '</div>' +
                '<ul class="date-line clearf date-month-body"></ul>' +
                '</div>' +
                '</div>');
        })();
        // 主体 dateline 的点击事件
        var fDateLineClick = function(){
            var el=$(this);
            // 设置开始和结束时间的值。nflag 参数来设置时间是加一个月（1）还是减一个月(-1)
            var fStepMonth = function(nflag){
                var oNewStart, oNewEnd;// 切换后要显示的新起止日期
                var oRefer = oCurStart || oCurEnd;// 参照日期，起止时间都存在时，以开始日期为参照
                if(oRefer){
                    oNewStart = new Date(oRefer.format('yyyy/MM/01')).add('M', nflag);// 开始日期设置为上/下个月的第一天
                    oNewEnd = new Date(oNewStart.add('M', 1).add('d', -1)); // 结束日期设为开始日期加1个月减1天
                    fSetDate(oNewStart, oNewEnd, true);
                }
            };
            if(el.hasClass('dateline-l')){// 切换到上个月
                fStepMonth(-1);
            }
            else if(el.hasClass('dateline-r')){// 切换到下个月
                fStepMonth(1);
            }
            else if(el.hasClass('dateline-c')){// 显示/隐藏主体
                if(triggerEl.hasClass('now')){
                    hideLayer();
                }else{
                    showLayer();
                }
            }
        };
        var triggerIptEl = layerEl.find('input.date-trigger');
        jqStartIpt = triggerIptEl.eq(0);
        jqEndIpt = triggerIptEl.eq(1);
        fSetDate(opts.startDate, opts.endDate);// 初始化 dateline 时间范围
        dateline.on('click.dateline','>div',fDateLineClick); //显示框.dateline的点击事件
        initCustom(layerEl.find('.date-container[data-type="custom"]'));
        initMonth(layerEl.find('.date-container[data-type="month"]'));
        initBody(layerEl);
        dateline.addClass('dateline').append(prevEl,triggerEl,nextEl,layerEl);
    };
    api = {
        //curStart: Date,// 开始日期对象，fSetDate() 中同步
        //curEnd: Date,// 结束日期对象，fSetDate() 中同步
        setDateData:fSetDate,// TODO del 兼容旧版本，现改为 fSetDate()
        fSetDate:fSetDate,// 设置日期
        fGetDate:fGetDate // 获取日期
    };
    initDateLine();
    return api;
};